creating ranks

the ranks will be a named num, with entrezgene_id as name and stat (Wald Test) as metric

getRanks <- function(res, annot) {
  # only taking genes which have entrezgene_ids assigned to them
  genes_with_entrez <- select(annot, GeneID, entrezgene_id) %>% 
    filter(!is.na(entrezgene_id))
  
  ranks <- as.data.frame(res) %>%
    tibble::rownames_to_column("GeneID") %>%
    merge(genes_with_entrez, by = "GeneID") %>%
    arrange(desc(stat)) %>% 
    select(entrezgene_id, stat) %>% 
    tibble::deframe() # creating a named num from two columns
  return(ranks)
}

ranks.gastroc <- getRanks(res.gastroc, annot)
ranks.soleus <- getRanks(res.soleus, annot)
# TODO: why (again) is the soleus gene count seemingly 300 below gastroc gene count / ranks count
# -> seems because of e.g. the cutoff at DESeq

ranks distribution

duplicate entrezgene_ids

duplicate entrezgene ids (multiple entrez are mapped to the same gene name)

Thus a quick look if any of these genes can be simply omitted. Like if the gene_type is “other” or “tRNA”

Since all ENSEMBL duplicates are also found in the gene_name duplicates, using the ENSEMBL as id for the ranks would reduce the number of duplicates by 107.

actually most of them are “protein coding” and will not be omitted. to be continued …

loading pathways

Pathways are provided by http://www.gsea-msigdb.org/gsea/msigdb/mouse/collections.jsp

For now the Canonical pathways are used. These gene sets represent biological a biological process. They are composed from the following databases taking a subset of CP:

database gene sets
BioCarta 252
Reactome 1249
WikiPathways 186

applying fgsea

fgseaRes <- fgsea(
  pathways = CGP,
  stats    = ranks,
  minSize  = 15,
  maxSize  = 200
)

Enrichment score plot

ordering pathways by padj values and using ES to

# ' obtain top pathways ordered by padj and use `ES` for up or down regulation
get_top_pathways <- function(fgseaRes, up = TRUE, pCutoff=params$pCutoff, n=10) {
  .updown <- ifelse(up, `>`, `<`)
  
  top.pathways <- fgseaRes %>%
    filter(.updown(ES,0), padj < pCutoff) %>%
    arrange(padj) %>% 
    slice_head(n=n)
  
 return(top.pathways) 
}

plot for top up and down regulated pathways

# ' plots top n enrichment plots for the given fgsea result
plot_top_enrichment <- function(fgseaRes, pathways, ranks, n = 9, up = TRUE) {
  # extracting the top n pathways
  top.pathways <- get_top_pathways(fgseaRes, up=up, pCutoff=params$pCutoff, n=n)
  
  plot.list <- list()
  # lims <- list("x" = c(0,17000), "y" = c(-0.8,0.0))
  
  for (i in 1:nrow(top.pathways)) {
    # filling plot.list with enrichmentPlots 
    # TODO: how can I use facet_wrap for this?
    pathway <- top.pathways[i]$pathway
    plt <- plotEnrichment(pathways[[pathway]], ranks) +
      # TODO: adjust yaxis to the same scale
      # TODO: keep axis.text.x only on the lower row
      # TODO: keep axis.text.y only on the right column
      theme(
        axis.title.x = element_blank(),
        axis.title.y = element_blank()
      ) # +
      # coord_cartesian(xlim = lims$x, ylim = lims$y)
    plot.list[[i]] <- plt
  }
    
  arrange_plts(plot.list)
}

# ' helper function to arragen the plot from the enrichment
arrange_plts <- function(plt.list) {
  nplts <- length(plt.list)
  plt <- plt.list[1]
  xlab <- plt$labels$x
  ylab <- plt$labels$y
  
  # set axis to the same scale
  lims <- list("x" = c(0, 17000), "y" = c(-0.8, 0.0))
  
  # remove axis
  
  # arrange the plots
  fig_labels <- LETTERS[1:nplts]
  
  patchwork::wrap_plots(plt.list, )
  
  figure <- ggpubr::ggarrange(plotlist = plt.list,
                              labels = fig_labels) %>%
    annotate_figure(left = text_grob(ylab, rot = 90),
                    bottom = text_grob(xlab))
 
  # TODO: remove all x-axis labels except lower row
  # get dimensions
  figure$layers
   
  return(figure)
}



# plot_labels <-
#     data.frame("label" = LETTERS[1:10], "pathway" = top.pathways)
# knitr::kable(caption = "plot labels", plot_labels)

gastroc up

plot_top_enrichment(fgseaRes.gastroc, CGP, ranks.gastroc, up=T)


# TODO: add plot labels to return argument of plot_top_enrichment (use list probably)
plot_labels <-
    data.frame("label" = LETTERS[1:9], "pathway" = get_top_pathways(fgseaRes.gastroc, up=T, n=9)$pathway)
knitr::kable(caption = "plot labels", plot_labels)
plot labels
label pathway
A WP_TYROBP_CAUSAL_NETWORK_IN_MICROGLIA
B WP_MICROGLIA_PATHOGEN_PHAGOCYTOSIS_PATHWAY
C WP_APOPTOSIS
D REACTOME_IMMUNOREGULATORY_INTERACTIONS_BETWEEN_A_LYMPHOID_AND_A_NON_LYMPHOID_CELL
E WP_FIBRIN_COMPLEMENT_RECEPTOR_3_SIGNALING_PATHWAY
F WP_CHEMOKINE_SIGNALING_PATHWAY
G BIOCARTA_TNFR2_PATHWAY
H REACTOME_DAP12_INTERACTIONS
I REACTOME_FCGAMMA_RECEPTOR_FCGR_DEPENDENT_PHAGOCYTOSIS

gastroc down

plot_top_enrichment(fgseaRes.gastroc, CGP, ranks.gastroc, up=F)

plot_labels <-
    data.frame("label" = LETTERS[1:9], "pathway" = get_top_pathways(fgseaRes.gastroc, up=F, n=9)$pathway)
knitr::kable(caption = "plot labels", plot_labels)
plot labels
label pathway
A REACTOME_THE_CITRIC_ACID_TCA_CYCLE_AND_RESPIRATORY_ELECTRON_TRANSPORT
B REACTOME_RESPIRATORY_ELECTRON_TRANSPORT_ATP_SYNTHESIS_BY_CHEMIOSMOTIC_COUPLING_AND_HEAT_PRODUCTION_BY_UNCOUPLING_PROTEINS
C REACTOME_RESPIRATORY_ELECTRON_TRANSPORT
D WP_ELECTRON_TRANSPORT_CHAIN
E REACTOME_COMPLEX_I_BIOGENESIS
F REACTOME_MITOCHONDRIAL_TRANSLATION
G REACTOME_KEAP1_NFE2L2_PATHWAY
H REACTOME_CELLULAR_RESPONSE_TO_HYPOXIA
I REACTOME_CELLULAR_RESPONSE_TO_CHEMICAL_STRESS

soleus up

plot_top_enrichment(fgseaRes.soleus, CGP, ranks.soleus, up=T)


plot_labels <-
    data.frame("label" = LETTERS[1:9], "pathway" = get_top_pathways(fgseaRes.soleus, up=T, n=9)$pathway)
knitr::kable(caption = "plot labels", plot_labels)
plot labels
label pathway
A REACTOME_SRP_DEPENDENT_COTRANSLATIONAL_PROTEIN_TARGETING_TO_MEMBRANE
B REACTOME_FORMATION_OF_A_POOL_OF_FREE_40S_SUBUNITS
C REACTOME_NONSENSE_MEDIATED_DECAY_NMD_INDEPENDENT_OF_THE_EXON_JUNCTION_COMPLEX_EJC
D WP_CYTOPLASMIC_RIBOSOMAL_PROTEINS
E WP_TYROBP_CAUSAL_NETWORK_IN_MICROGLIA
F REACTOME_EUKARYOTIC_TRANSLATION_INITIATION
G REACTOME_NONSENSE_MEDIATED_DECAY_NMD
H REACTOME_MAJOR_PATHWAY_OF_RRNA_PROCESSING_IN_THE_NUCLEOLUS_AND_CYTOSOL
I REACTOME_PRC2_METHYLATES_HISTONES_AND_DNA

soleus down

plot_top_enrichment(fgseaRes.soleus, CGP, ranks.soleus, up=F)

plot_labels <-
    data.frame("label" = LETTERS[1:9], "pathway" = get_top_pathways(fgseaRes.soleus, up=F, n=9)$pathway)
knitr::kable(caption = "plot labels", plot_labels)
plot labels
label pathway
A REACTOME_KEAP1_NFE2L2_PATHWAY
B REACTOME_CELLULAR_RESPONSE_TO_CHEMICAL_STRESS
C REACTOME_GLI3_IS_PROCESSED_TO_GLI3R_BY_THE_PROTEASOME
D REACTOME_UBIQUITIN_MEDIATED_DEGRADATION_OF_PHOSPHORYLATED_CDC25A
E REACTOME_RUNX1_REGULATES_TRANSCRIPTION_OF_GENES_INVOLVED_IN_DIFFERENTIATION_OF_HSCS
F REACTOME_CELLULAR_RESPONSE_TO_HYPOXIA
G REACTOME_DEGRADATION_OF_DVL
H REACTOME_ABC_FAMILY_PROTEINS_MEDIATED_TRANSPORT
I REACTOME_ASYMMETRIC_LOCALIZATION_OF_PCP_PROTEINS

GSEA table plot

gastroc

top significant pathways:

# creating up and down regulated pathway vectors separately to maintain order

topUp <- get_top_pathways(fgseaRes.gastroc, up=T, pCutoff = params$pCutoff, n=10)
topDown <- get_top_pathways(fgseaRes.gastroc, up=F, pCutoff = params$pCutoff, n=10)
topPathways <- bind_rows(topUp, topDown) %>%
  arrange(-NES) %>%
  pull(pathway)

plotGseaTable(
  pathways = CGP[topPathways],
  stats = ranks.gastroc,
  fgseaRes = fgseaRes.gastroc,
  gseaParam = 0.5,
  render = TRUE
) %>%
  ggpubr::as_ggplot() # needed since, for whatever reason only `NULL` gets returned if `plotGseaTable` is rendered inline

soleus

top significant pathways:

# creating up and down regulated pathway vectors separately to maintain order

topUp <- get_top_pathways(fgseaRes.soleus, up=T, pCutoff = params$pCutoff, n=10)
topDown <- get_top_pathways(fgseaRes.soleus, up=F, pCutoff = params$pCutoff, n=10)
topPathways <- bind_rows(topUp, topDown) %>%
  arrange(-NES) %>%
  pull(pathway)

plotGseaTable(
  pathways = CGP[topPathways],
  stats = ranks.soleus,
  fgseaRes = fgseaRes.soleus,
  gseaParam = 0.5,
  render = TRUE
) %>%
  ggpubr::as_ggplot() # needed since, for whatever reason only `NULL` gets returned if `plotGseaTable` is rendered inline

most differential regulated pathways, both tissues

using NES from the fgsea result filtering on the set `pCutoff=`0.01 yields the following plot:


pCutoff <- params$pCutoff
fgseaRes.combined <- merge(
  data.frame(fgseaRes.gastroc[, c("pathway", "NES", "padj")]),
  data.frame(fgseaRes.soleus[, c("pathway", "NES", "padj")]),
  by = "pathway",
  suffixes = c(".ga", ".sol")
) %>%
  filter(padj.ga < pCutoff | padj.sol < pCutoff) %>%
  mutate(
    diff.exp = case_when(
      NES.ga  < 0 & NES.sol < 0 & padj.ga < pCutoff & padj.sol < pCutoff ~ "both down",
      NES.ga  > 0 & NES.sol > 0 & padj.ga < pCutoff & padj.sol < pCutoff ~ "both up",
      NES.ga  < 0 & NES.sol > 0 & padj.ga < pCutoff & padj.sol < pCutoff ~ "ga down, sol up",
      NES.ga  > 0 & NES.sol < 0 & padj.ga < pCutoff & padj.sol < pCutoff ~ "ga up, sol down",
                                  padj.ga < pCutoff & padj.sol > pCutoff ~ "only gastroc",
      # NES.ga  > 0 &               padj.ga < pCutoff & padj.sol > pCutoff ~ "ga up",
                                  padj.ga > pCutoff & padj.sol < pCutoff ~ "only soleus",
      # NES.sol > 0 &               padj.ga > pCutoff & padj.sol < pCutoff ~ "sol up",
      TRUE ~ "different"
    )
  )

# final plot
p <- ggplot(fgseaRes.combined, aes(x = NES.ga, y = NES.sol, text=pathway)) +
  geom_vline(xintercept = 0) + 
  geom_hline(yintercept = 0) + 
  geom_point(aes(color = diff.exp)) +
  # scale_color_manual(values = c("red", "chartreuse1", "bisque", "royalblue")) +
  labs(x = "gastroc", y = "soleus") +
  # ggrepel::geom_label_repel(max.overlaps = 20) + 
  ggtitle(label = "NES")

plotly::ggplotly(p, tooltip = "all")

barplot

ggplot(fgseaRes.combined, aes(x = diff.exp)) +
  geom_bar(aes(fill = diff.exp))

list of all significant pathways

pathways will be sorted here by the absolute sum of the NES…

both down

res.combined %>% 
  filter(diff.exp == "both down") %>% 
  mutate(NES = abs(NES.ga) + abs(NES.sol)) %>% 
  select(pathway, NES) %>% 
  arrange(desc(NES)) %>%
  rmarkdown::paged_table(options = list(cols.print = 2))

both up

res.combined %>% 
  filter(diff.exp == "both up") %>% 
  mutate(NES = abs(NES.ga) + abs(NES.sol)) %>% 
  select(pathway, NES) %>% 
  arrange(desc(NES)) %>%
  rmarkdown::paged_table(options = list(cols.min.print = 2))

only gastroc

fgseaRes.combined %>% 
  filter(diff.exp == "only gastroc") %>% 
  mutate(NES = abs(NES.ga) + abs(NES.sol)) %>% 
  select(pathway, NES) %>% 
  arrange(desc(NES)) %>%
  rmarkdown::paged_table(options = list(cols.min.print = 2))

only soleus

fgseaRes.combined %>% 
  filter(diff.exp == "only soleus") %>% 
  mutate(NES = abs(NES.ga) + abs(NES.sol)) %>% 
  select(pathway, NES) %>% 
  arrange(desc(NES)) %>%
  rmarkdown::paged_table(options = list(cols.min.print = 2))

currentTODOs

[ ] looking at duplicate entrezgene_ids
[ ] finding optimal maxSize (one sided curve)
[ ] find out biological meaning of significant pathways

LS0tCnRpdGxlOiAiR1NFQSBhbmFseXNpcyIKYXV0aG9yOiAiTmljayBEaWVyY2tzZW4iCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQpwYXJhbXM6CiAgcEN1dG9mZjoKICAgIHZhbHVlOiAwLjAxCiAgZmdzZWEubWF4U2l6ZToKICAgIGxhYmVsOiAicGFyYW1ldGVyIGZvciBgZmdzZWFgOiAnTWF4aW1hbCBzaXplIG9mIGEgZ2VuZSBzZXQgdG8gdGVzdC4gQWxsIHBhdGh3YXlzIGFib3ZlIHRoZSB0aHJlc2hvbGQgYXJlIGV4Y2x1ZGVkLiciCiAgICB2YWx1ZTogMjAwCiAgcmVldmFsdWF0ZToKICAgIGxhYmVsOiBTaG91bGQgYWxsIGNhbGN1bGF0aW9ucyBhbmQgZGF0YWJhc2UgbG9va3VwcyBiZSBkb25lIGFnYWluIG9yIGp1c3QgZS5nLgogICAgICBncmFwaGljcyByZWdlbmVyYXRlZAogICAgdmFsdWU6IG5vCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeShkcGx5cikKbGlicmFyeShmZ3NlYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkocGxvdGx5LCBpbmNsdWRlLm9ubHkgPSAiZ2dwbG90bHkiKQpgYGAKCmBgYHtyIGxvYWRfcmRhdGEsIGluY2x1ZGU9RkFMU0V9CgpwZXJmb3JtX2Znc2VhIDwtIHBhcmFtcyRyZWV2YWx1YXRlCgpsb2FkKCIuL2RhdGEvUm9iamVjdHMvMDJfYW5ub3QuUkRhdGEiKSAjIGZvciBlbnRyZXpnZW5lX2lkCmxvYWQoIi4vZGF0YS9Sb2JqZWN0cy8wM19ERFMuUkRhdGEiKSAgICMgZm9yIGBzdGF0YCB2YWx1ZXMgZnJvbSBERVNlcSByZXN1bHRzCmBgYAoKIyBjcmVhdGluZyByYW5rcwoKdGhlIGByYW5rc2Agd2lsbCBiZSBhIGBuYW1lZCBudW1gLCB3aXRoIGBlbnRyZXpnZW5lX2lkYCBhcyBuYW1lIGFuZCBgc3RhdGAgKFdhbGQgVGVzdCkgYXMgbWV0cmljCgpgYGB7ciBnZXR0aW5nX3JhbmtzfQpnZXRSYW5rcyA8LSBmdW5jdGlvbihyZXMsIGFubm90KSB7CiAgIyBvbmx5IHRha2luZyBnZW5lcyB3aGljaCBoYXZlIGVudHJlemdlbmVfaWRzIGFzc2lnbmVkIHRvIHRoZW0KICBnZW5lc193aXRoX2VudHJleiA8LSBzZWxlY3QoYW5ub3QsIEdlbmVJRCwgZW50cmV6Z2VuZV9pZCkgJT4lIAogICAgZmlsdGVyKCFpcy5uYShlbnRyZXpnZW5lX2lkKSkKICAKICByYW5rcyA8LSBhcy5kYXRhLmZyYW1lKHJlcykgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiR2VuZUlEIikgJT4lCiAgICBtZXJnZShnZW5lc193aXRoX2VudHJleiwgYnkgPSAiR2VuZUlEIikgJT4lCiAgICBhcnJhbmdlKGRlc2Moc3RhdCkpICU+JSAKICAgIHNlbGVjdChlbnRyZXpnZW5lX2lkLCBzdGF0KSAlPiUgCiAgICB0aWJibGU6OmRlZnJhbWUoKSAjIGNyZWF0aW5nIGEgbmFtZWQgbnVtIGZyb20gdHdvIGNvbHVtbnMKICByZXR1cm4ocmFua3MpCn0KCnJhbmtzLmdhc3Ryb2MgPC0gZ2V0UmFua3MocmVzLmdhc3Ryb2MsIGFubm90KQpyYW5rcy5zb2xldXMgPC0gZ2V0UmFua3MocmVzLnNvbGV1cywgYW5ub3QpCiMgVE9ETzogd2h5IChhZ2FpbikgaXMgdGhlIHNvbGV1cyBnZW5lIGNvdW50IHNlZW1pbmdseSAzMDAgYmVsb3cgZ2FzdHJvYyBnZW5lIGNvdW50IC8gcmFua3MgY291bnQKIyAtPiBzZWVtcyBiZWNhdXNlIG9mIGUuZy4gdGhlIGN1dG9mZiBhdCBERVNlcQpgYGAKCiMjIHJhbmtzIGRpc3RyaWJ1dGlvbgoKYGBge3IgcmFua3NfYmFycGxvdCwgZWNobz1GQUxTRX0KIyBubyBpZGVhIGhvdyB0byBwbG90IHRoaXMgd2l0aCBnZ3Bsb3QgKG9uZSBjb2x1bW4pIC4uLgoKIyBnZ3B1YnI6OmdnYXJyYW5nZSgKIyBUT0RPOiBwcmV0dGlmeQojIGJhcnBsb3QuZmlsZSA8LSAiLi9wbG90cy8wNF9iYXJwbG90X3JhbmtzLnBuZyIKIyBpZiAoIWZpbGUuZXhpc3RzKGJhcnBsb3QuZmlsZSkpIHsKIyAgIHBuZyhiYXJwbG90LmZpbGUpCiAgICBwYXIobWZyb3cgPSBjKDEsIDIpKQogICAgYmFycGxvdChzb3J0KHJhbmtzLmdhc3Ryb2MsIGRlY3JlYXNpbmcgPSBUKSwgbWFpbiA9ICJXYWxkIFRlc3QgcmFua3MsIGdhc3Ryb2MiKQogICAgYmFycGxvdChzb3J0KHJhbmtzLnNvbGV1cywgZGVjcmVhc2luZyA9IFQpLCBtYWluID0gIldhbGQgVGVzdCByYW5rcywgc29sZXVzIikKICAjIGRldi5vZmYoKQojIH0KIyApCmBgYAoKCiMjIGR1cGxpY2F0ZSBlbnRyZXpnZW5lX2lkcwoKZHVwbGljYXRlIGVudHJlemdlbmUgaWRzIChtdWx0aXBsZSBlbnRyZXogYXJlIG1hcHBlZCB0byB0aGUgc2FtZSBnZW5lIG5hbWUpCgpUaHVzIGEgcXVpY2sgbG9vayBpZiBhbnkgb2YgdGhlc2UgZ2VuZXMgY2FuIGJlIHNpbXBseSBvbWl0dGVkLiBMaWtlIGlmIHRoZSBnZW5lX3R5cGUgaXMgIm90aGVyIiBvciAidFJOQSIKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgVE9ETzogZG8gbm90IHVzZSBhbm5vdCwgYnV0IHRoZSBtZXJnZT8gKGFjdHVhbGx5IHVzZWQgZ2VuZXMhKQojIGZpcnN0IGxvb2tpbmcgYXQgRU5TRU1CTDoKZHVwbGljYXRlX0VOU0VNQkwgPC0gYW5ub3RbZHVwbGljYXRlZChhbm5vdCRHZW5lSUQpLCBdICU+JQogIGRwbHlyOjpncm91cF9ieShHZW5lSUQsIGdlbmVfYmlvdHlwZSkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpCgojIGxvb2tpbmcgYXQgZ2VuZV9uYW1lcyhleHQpOgpkdXBsaWNhdGVfZ2VuZU5hbWVzIDwtIGFubm90W2R1cGxpY2F0ZWQoYW5ub3QkZXh0ZXJuYWxfZ2VuZV9uYW1lKSwgXSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KEdlbmVJRCwgZ2VuZV9iaW90eXBlKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkKCiMgZHVwbGljYXRlcyBoYXZlIGFsd2F5cyB0aGUgc2FtZSBnZW5lX2Jpb3R5cGUhIChpbiBubyBtb3JlIHRoYW4gb25lIGdyb3VwIG9jY3VycyB0aGUgc2FtZSBHZW5lSUQpCmFueUR1cGxpY2F0ZWQoZHVwbGljYXRlX0VOU0VNQkwkR2VuZUlEKSAgICMgMAphbnlEdXBsaWNhdGVkKGR1cGxpY2F0ZV9nZW5lTmFtZXMkR2VuZUlEKSAjIDAKCiMgYXJlIGFsbCBnZW5lX25hbWUgZHVwbGljYXRlcyBpbiBFTlMgZHVwbGljYXRlcz8Kc3VtKGR1cGxpY2F0ZV9FTlNFTUJMJEdlbmVJRCAlaW4lIGR1cGxpY2F0ZV9nZW5lTmFtZXMkR2VuZUlEKSAlPiUKICBzcHJpbnRmKAogICAgIiVkIC8gJWQgRU5TRU1CTCBkdXBsaWNhdGlvbnMgYXJlIGFsc28gaW5jbHVkZWQgaW4gdGhlIGR1cGxpY2F0ZWQgZ2VuZV9uYW1lcyIsCiAgICAuLAogICAgbnJvdyhkdXBsaWNhdGVfRU5TRU1CTCkKICApCgpkaWZmX0VOU19nZW5lIDwtIG5yb3coZHVwbGljYXRlX2dlbmVOYW1lcykgLSBucm93KGR1cGxpY2F0ZV9FTlNFTUJMKQpgYGAKClNpbmNlIGFsbCBFTlNFTUJMIGR1cGxpY2F0ZXMgYXJlIGFsc28gZm91bmQgaW4gdGhlIGdlbmVfbmFtZSBkdXBsaWNhdGVzLCB1c2luZyB0aGUgRU5TRU1CTCBhcyBpZCBmb3IgdGhlIHJhbmtzIHdvdWxkIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGR1cGxpY2F0ZXMgYnkgYHIgZGlmZl9FTlNfZ2VuZWAuCgphY3R1YWxseSBtb3N0IG9mIHRoZW0gYXJlICJwcm90ZWluIGNvZGluZyIgYW5kIHdpbGwgbm90IGJlIG9taXR0ZWQuCnRvIGJlIGNvbnRpbnVlZCAuLi4KCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgVE9ETzogZG8gYSBlLmcuIGJhci1wbG90IHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBvZiBnZW5lX2Jpb3R5cGVzIG9mIHRoZSBkdXBsaWNhdGVzCiMgICAgICAgdnMgdGhlIHdob2xlIGdlbm9tZQojIGNyZWF0ZSB0aGUgZGYgZm9yIHBsb3R0aW5nCgpFTlMgPC0gZHVwbGljYXRlX0VOU0VNQkwgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbmVfYmlvdHlwZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gc3VtKG4pKQoKCkdOIDwtIGR1cGxpY2F0ZV9nZW5lTmFtZXMgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGdlbmVfYmlvdHlwZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gc3VtKG4pKQoKCgpnZ3Bsb3QoZGF0YT1kZjIsIGFlcyh4PWRvc2UsIHk9bGVuLCBmaWxsPXN1cHApKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQoKYGBgCgojIGxvYWRpbmcgcGF0aHdheXMKClBhdGh3YXlzIGFyZSBwcm92aWRlZCBieSA8aHR0cDovL3d3dy5nc2VhLW1zaWdkYi5vcmcvZ3NlYS9tc2lnZGIvbW91c2UvY29sbGVjdGlvbnMuanNwPgoKRm9yIG5vdyB0aGUgQ2Fub25pY2FsIHBhdGh3YXlzIGFyZSB1c2VkLiBUaGVzZSBnZW5lIHNldHMgcmVwcmVzZW50IGJpb2xvZ2ljYWwgYSBiaW9sb2dpY2FsIHByb2Nlc3MuIFRoZXkgYXJlIGNvbXBvc2VkIGZyb20gdGhlIGZvbGxvd2luZyBkYXRhYmFzZXMgdGFraW5nIGEgc3Vic2V0IG9mIENQOgoKfCBkYXRhYmFzZSAgICAgfCBnZW5lIHNldHMgfAp8LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS18CnwgQmlvQ2FydGEgICAgIHwgMjUyICAgICAgIHwKfCBSZWFjdG9tZSAgICAgfCAxMjQ5ICAgICAgfAp8IFdpa2lQYXRod2F5cyB8IDE4NiAgICAgICB8CgpgYGB7ciByZWFkaW5nX3BhdGh3YXlzLCBpbmNsdWRlPUZBTFNFfQpDR1AgPC0gcXVzYWdlOjpyZWFkLmdtdCgiLi9kYXRhL3BhdGh3YXlzL20yLmNwLnYyMDIyLjEuTW0uZW50cmV6LmdtdCIpICMgY2Fub25pY2FsIHBhdGh3YXlzICgxNjg3IGdlbmUgc2V0cykKCiMgcHJldmlvdXNseSB1c2VkIHBhdGh3YXlzCiMgTUggPC0gcXVzYWdlOjpyZWFkLmdtdCgiLi9kYXRhL3BhdGh3YXlzL21oLmFsbC52MjAyMi4xLk1tLmVudHJlei5nbXQiKSAjIDUwIGhhbGxtYXJrIGdlbmVzCiMgTTIgPC0gcXVzYWdlOjpyZWFkLmdtdCgiLi9kYXRhL3BhdGh3YXlzL20yLmFsbC52MjAyMi4xLk1tLmVudHJlei5nbXQiKSAjIGN1cmF0ZWQgZ2VuZSBzZXQgd2l0aCAyNjAwIGdlbmVzCmBgYAoKIyBhcHBseWluZyBmZ3NlYQoKICAgIGZnc2VhUmVzIDwtIGZnc2VhKAogICAgICBwYXRod2F5cyA9IENHUCwKICAgICAgc3RhdHMgICAgPSByYW5rcywKICAgICAgbWluU2l6ZSAgPSAxNSwKICAgICAgbWF4U2l6ZSAgPSBgciBwYXJhbXMkZmdzZWEubWF4U2l6ZWAKICAgICkKCmBgYHtyIGZnc2VhX3Jlc19sb2FkLCBpbmNsdWRlPUZBTFNFfQojICcgcmV0dXJucyB0aGUgZmdzZWEgcmVzdWx0CiMgJyBgdGlzc3VlYCBuZWVkcyB0byBiZSBvbmUgb2YgeyJnYXN0cm9jIiwgInNvbGV1cyJ9CiMgJyBBbHNvIHNhdmVzIHRoZSByZXN1bHRzIGxvY2FsbHkgdG8gYXZvaWQgdG9vIG11Y2ggcXVlcmllcwpnZXRfZmdzZWFSZXMgPC0gZnVuY3Rpb24odGlzc3VlLCByYW5rcywgbWF4U2l6ZT1wYXJhbXMkZmdzZWEubWF4U2l6ZSwgcGF0aHdheXM9Q0dQLCBzYXZlUmVzdWx0cz1UKSB7CiAgcm9iamVjdHMucGF0aCA8LSBmaWxlLnBhdGgoIi4iLCAiZGF0YSIsICJSb2JqZWN0cyIpCiAgZmdzZWFSZXMucGF0aCA8LSBmaWxlLnBhdGgoCiAgICByb2JqZWN0cy5wYXRoLAogICAgIjA0X2Znc2VhX3Jlc3VsdHMiLAogICAgdGlzc3VlLAogICAgcGFzdGUwKCJtYXhTaXplXyIsIHBhcmFtcyRmZ3NlYS5tYXhTaXplLCAiLnJkcyIpCiAgKQogIGZnc2VhUmVzLmRpciA8LSBkaXJuYW1lKGZnc2VhUmVzLnBhdGgpCiAgCiAgaWYgKGZpbGUuZXhpc3RzKGZnc2VhUmVzLnBhdGgpICYgIXBhcmFtcyRyZWV2YWx1YXRlKSB7CiAgICBtZXNzYWdlKCJmZ3NlYSByZXN1bHQgZm91bmQgaW4gZGlyZWN0b3J5ISBQcmV2aW91cyB2ZXJzaW9uIHdpbGwgYmUgbG9hZGVkIC4uLiIpCiAgICByZXR1cm4ocmVhZFJEUyhmaWxlID0gZmdzZWFSZXMucGF0aCkpCiAgfSBlbHNlIHsKICAgICMgcGVyZm9ybSBmZ3NlYSBhbmQgc2F2ZSB0aGUgcmVzdWx0CiAgICBmZ3NlYVJlcyA8LSBmZ3NlYSgKICAgICAgcGF0aHdheXMgPSBwYXRod2F5cywKICAgICAgc3RhdHMgICAgPSByYW5rcywKICAgICAgbWluU2l6ZSAgPSAxNSwKICAgICAgbWF4U2l6ZSAgPSBtYXhTaXplCiAgICApCiAgICAKICAgIGlmIChzYXZlUmVzdWx0cykgewogICAgICBpZiAoIWRpci5leGlzdHMoZmdzZWFSZXMuZGlyKSkKICAgICAgICBkaXIuY3JlYXRlKGZnc2VhUmVzLmRpciwgcmVjdXJzaXZlID0gVCkKICAgICAgc2F2ZVJEUyhmZ3NlYVJlcywgZmlsZSA9IGZnc2VhUmVzLnBhdGgpCiAgICB9CiAgICByZXR1cm4oZmdzZWFSZXMpCiAgfQp9CgpmZ3NlYVJlcy5nYXN0cm9jIDwtIGdldF9mZ3NlYVJlcygiZ2FzdHJvYyIsIHJhbmtzLmdhc3Ryb2MsIHBhdGh3YXlzPUNHUCkKZmdzZWFSZXMuc29sZXVzIDwtIGdldF9mZ3NlYVJlcygic29sZXVzIiwgcmFua3Muc29sZXVzLCBwYXRod2F5cz1DR1ApCmBgYAoKIyMgRW5yaWNobWVudCBzY29yZSBwbG90CgpvcmRlcmluZyBwYXRod2F5cyBieSBwYWRqIHZhbHVlcyBhbmQgdXNpbmcgYEVTYCB0bwoKYGBge3IgZm5fZ2V0X3RvcF9wYXRod2F5c30KIyAnIG9idGFpbiB0b3AgcGF0aHdheXMgb3JkZXJlZCBieSBwYWRqIGFuZCB1c2UgYEVTYCBmb3IgdXAgb3IgZG93biByZWd1bGF0aW9uCmdldF90b3BfcGF0aHdheXMgPC0gZnVuY3Rpb24oZmdzZWFSZXMsIHVwID0gVFJVRSwgcEN1dG9mZj1wYXJhbXMkcEN1dG9mZiwgbj0xMCkgewogIC51cGRvd24gPC0gaWZlbHNlKHVwLCBgPmAsIGA8YCkKICAKICB0b3AucGF0aHdheXMgPC0gZmdzZWFSZXMgJT4lCiAgICBmaWx0ZXIoLnVwZG93bihFUywwKSwgcGFkaiA8IHBDdXRvZmYpICU+JQogICAgYXJyYW5nZShwYWRqKSAlPiUgCiAgICBzbGljZV9oZWFkKG49bikKICAKIHJldHVybih0b3AucGF0aHdheXMpIAp9CmBgYAoKcGxvdCBmb3IgdG9wIHVwIGFuZCBkb3duIHJlZ3VsYXRlZCBwYXRod2F5cwoKYGBge3J9CiMgJyBwbG90cyB0b3AgbiBlbnJpY2htZW50IHBsb3RzIGZvciB0aGUgZ2l2ZW4gZmdzZWEgcmVzdWx0CnBsb3RfdG9wX2VucmljaG1lbnQgPC0gZnVuY3Rpb24oZmdzZWFSZXMsIHBhdGh3YXlzLCByYW5rcywgbiA9IDksIHVwID0gVFJVRSkgewogICMgZXh0cmFjdGluZyB0aGUgdG9wIG4gcGF0aHdheXMKICB0b3AucGF0aHdheXMgPC0gZ2V0X3RvcF9wYXRod2F5cyhmZ3NlYVJlcywgdXA9dXAsIHBDdXRvZmY9cGFyYW1zJHBDdXRvZmYsIG49bikKICAKICBwbG90Lmxpc3QgPC0gbGlzdCgpCiAgIyBsaW1zIDwtIGxpc3QoIngiID0gYygwLDE3MDAwKSwgInkiID0gYygtMC44LDAuMCkpCiAgCiAgZm9yIChpIGluIDE6bnJvdyh0b3AucGF0aHdheXMpKSB7CiAgICAjIGZpbGxpbmcgcGxvdC5saXN0IHdpdGggZW5yaWNobWVudFBsb3RzIAogICAgIyBUT0RPOiBob3cgY2FuIEkgdXNlIGZhY2V0X3dyYXAgZm9yIHRoaXM/CiAgICBwYXRod2F5IDwtIHRvcC5wYXRod2F5c1tpXSRwYXRod2F5CiAgICBwbHQgPC0gcGxvdEVucmljaG1lbnQocGF0aHdheXNbW3BhdGh3YXldXSwgcmFua3MpICsKICAgICAgIyBUT0RPOiBhZGp1c3QgeWF4aXMgdG8gdGhlIHNhbWUgc2NhbGUKICAgICAgIyBUT0RPOiBrZWVwIGF4aXMudGV4dC54IG9ubHkgb24gdGhlIGxvd2VyIHJvdwogICAgICAjIFRPRE86IGtlZXAgYXhpcy50ZXh0Lnkgb25seSBvbiB0aGUgcmlnaHQgY29sdW1uCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgKSAjICsKICAgICAgIyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGxpbXMkeCwgeWxpbSA9IGxpbXMkeSkKICAgIHBsb3QubGlzdFtbaV1dIDwtIHBsdAogIH0KICAgIAogIGFycmFuZ2VfcGx0cyhwbG90Lmxpc3QpCn0KCiMgJyBoZWxwZXIgZnVuY3Rpb24gdG8gYXJyYWdlbiB0aGUgcGxvdCBmcm9tIHRoZSBlbnJpY2htZW50CmFycmFuZ2VfcGx0cyA8LSBmdW5jdGlvbihwbHQubGlzdCkgewogIG5wbHRzIDwtIGxlbmd0aChwbHQubGlzdCkKICBwbHQgPC0gcGx0Lmxpc3RbMV0KICB4bGFiIDwtIHBsdCRsYWJlbHMkeAogIHlsYWIgPC0gcGx0JGxhYmVscyR5CiAgCiAgIyBzZXQgYXhpcyB0byB0aGUgc2FtZSBzY2FsZQogIGxpbXMgPC0gbGlzdCgieCIgPSBjKDAsIDE3MDAwKSwgInkiID0gYygtMC44LCAwLjApKQogIAogICMgcmVtb3ZlIGF4aXMKICAKICAjIGFycmFuZ2UgdGhlIHBsb3RzCiAgZmlnX2xhYmVscyA8LSBMRVRURVJTWzE6bnBsdHNdCiAgCiAgcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsdC5saXN0LCApCiAgCiAgZmlndXJlIDwtIGdncHVicjo6Z2dhcnJhbmdlKHBsb3RsaXN0ID0gcGx0Lmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGZpZ19sYWJlbHMpICU+JQogICAgYW5ub3RhdGVfZmlndXJlKGxlZnQgPSB0ZXh0X2dyb2IoeWxhYiwgcm90ID0gOTApLAogICAgICAgICAgICAgICAgICAgIGJvdHRvbSA9IHRleHRfZ3JvYih4bGFiKSkKIAogICMgVE9ETzogcmVtb3ZlIGFsbCB4LWF4aXMgbGFiZWxzIGV4Y2VwdCBsb3dlciByb3cKICAjIGdldCBkaW1lbnNpb25zCiAgZmlndXJlJGxheWVycwogICAKICByZXR1cm4oZmlndXJlKQp9CgoKCiMgcGxvdF9sYWJlbHMgPC0KIyAgICAgZGF0YS5mcmFtZSgibGFiZWwiID0gTEVUVEVSU1sxOjEwXSwgInBhdGh3YXkiID0gdG9wLnBhdGh3YXlzKQojIGtuaXRyOjprYWJsZShjYXB0aW9uID0gInBsb3QgbGFiZWxzIiwgcGxvdF9sYWJlbHMpCgpgYGAKCiMjIyBnYXN0cm9jIHVwCgpgYGB7cn0KcGxvdF90b3BfZW5yaWNobWVudChmZ3NlYVJlcy5nYXN0cm9jLCBDR1AsIHJhbmtzLmdhc3Ryb2MsIHVwPVQpCgojIFRPRE86IGFkZCBwbG90IGxhYmVscyB0byByZXR1cm4gYXJndW1lbnQgb2YgcGxvdF90b3BfZW5yaWNobWVudCAodXNlIGxpc3QgcHJvYmFibHkpCnBsb3RfbGFiZWxzIDwtCiAgICBkYXRhLmZyYW1lKCJsYWJlbCIgPSBMRVRURVJTWzE6OV0sICJwYXRod2F5IiA9IGdldF90b3BfcGF0aHdheXMoZmdzZWFSZXMuZ2FzdHJvYywgdXA9VCwgbj05KSRwYXRod2F5KQprbml0cjo6a2FibGUoY2FwdGlvbiA9ICJwbG90IGxhYmVscyIsIHBsb3RfbGFiZWxzKQpgYGAKCiMjIyBnYXN0cm9jIGRvd24KCmBgYHtyfQpwbG90X3RvcF9lbnJpY2htZW50KGZnc2VhUmVzLmdhc3Ryb2MsIENHUCwgcmFua3MuZ2FzdHJvYywgdXA9RikKcGxvdF9sYWJlbHMgPC0KICAgIGRhdGEuZnJhbWUoImxhYmVsIiA9IExFVFRFUlNbMTo5XSwgInBhdGh3YXkiID0gZ2V0X3RvcF9wYXRod2F5cyhmZ3NlYVJlcy5nYXN0cm9jLCB1cD1GLCBuPTkpJHBhdGh3YXkpCmtuaXRyOjprYWJsZShjYXB0aW9uID0gInBsb3QgbGFiZWxzIiwgcGxvdF9sYWJlbHMpCmBgYAoKIyMjIHNvbGV1cyB1cAoKYGBge3J9CnBsb3RfdG9wX2VucmljaG1lbnQoZmdzZWFSZXMuc29sZXVzLCBDR1AsIHJhbmtzLnNvbGV1cywgdXA9VCkKCnBsb3RfbGFiZWxzIDwtCiAgICBkYXRhLmZyYW1lKCJsYWJlbCIgPSBMRVRURVJTWzE6OV0sICJwYXRod2F5IiA9IGdldF90b3BfcGF0aHdheXMoZmdzZWFSZXMuc29sZXVzLCB1cD1ULCBuPTkpJHBhdGh3YXkpCmtuaXRyOjprYWJsZShjYXB0aW9uID0gInBsb3QgbGFiZWxzIiwgcGxvdF9sYWJlbHMpCmBgYAoKIyMjIHNvbGV1cyBkb3duCgpgYGB7cn0KcGxvdF90b3BfZW5yaWNobWVudChmZ3NlYVJlcy5zb2xldXMsIENHUCwgcmFua3Muc29sZXVzLCB1cD1GKQpwbG90X2xhYmVscyA8LQogICAgZGF0YS5mcmFtZSgibGFiZWwiID0gTEVUVEVSU1sxOjldLCAicGF0aHdheSIgPSBnZXRfdG9wX3BhdGh3YXlzKGZnc2VhUmVzLnNvbGV1cywgdXA9Riwgbj05KSRwYXRod2F5KQprbml0cjo6a2FibGUoY2FwdGlvbiA9ICJwbG90IGxhYmVscyIsIHBsb3RfbGFiZWxzKQpgYGAKCiMjIEdTRUEgdGFibGUgcGxvdAoKIyMjIGdhc3Ryb2MKCnRvcCBzaWduaWZpY2FudCBwYXRod2F5czoKCmBgYHtyfQojIGNyZWF0aW5nIHVwIGFuZCBkb3duIHJlZ3VsYXRlZCBwYXRod2F5IHZlY3RvcnMgc2VwYXJhdGVseSB0byBtYWludGFpbiBvcmRlcgoKdG9wVXAgPC0gZ2V0X3RvcF9wYXRod2F5cyhmZ3NlYVJlcy5nYXN0cm9jLCB1cD1ULCBwQ3V0b2ZmID0gcGFyYW1zJHBDdXRvZmYsIG49MTApCnRvcERvd24gPC0gZ2V0X3RvcF9wYXRod2F5cyhmZ3NlYVJlcy5nYXN0cm9jLCB1cD1GLCBwQ3V0b2ZmID0gcGFyYW1zJHBDdXRvZmYsIG49MTApCnRvcFBhdGh3YXlzIDwtIGJpbmRfcm93cyh0b3BVcCwgdG9wRG93bikgJT4lCiAgYXJyYW5nZSgtTkVTKSAlPiUKICBwdWxsKHBhdGh3YXkpCgpwbG90R3NlYVRhYmxlKAogIHBhdGh3YXlzID0gQ0dQW3RvcFBhdGh3YXlzXSwKICBzdGF0cyA9IHJhbmtzLmdhc3Ryb2MsCiAgZmdzZWFSZXMgPSBmZ3NlYVJlcy5nYXN0cm9jLAogIGdzZWFQYXJhbSA9IDAuNSwKICByZW5kZXIgPSBUUlVFCikgJT4lCiAgZ2dwdWJyOjphc19nZ3Bsb3QoKSAjIG5lZWRlZCBzaW5jZSwgZm9yIHdoYXRldmVyIHJlYXNvbiBvbmx5IGBOVUxMYCBnZXRzIHJldHVybmVkIGlmIGBwbG90R3NlYVRhYmxlYCBpcyByZW5kZXJlZCBpbmxpbmUKYGBgCgojIyMgc29sZXVzCgp0b3Agc2lnbmlmaWNhbnQgcGF0aHdheXM6CgpgYGB7cn0KIyBjcmVhdGluZyB1cCBhbmQgZG93biByZWd1bGF0ZWQgcGF0aHdheSB2ZWN0b3JzIHNlcGFyYXRlbHkgdG8gbWFpbnRhaW4gb3JkZXIKCnRvcFVwIDwtIGdldF90b3BfcGF0aHdheXMoZmdzZWFSZXMuc29sZXVzLCB1cD1ULCBwQ3V0b2ZmID0gcGFyYW1zJHBDdXRvZmYsIG49MTApCnRvcERvd24gPC0gZ2V0X3RvcF9wYXRod2F5cyhmZ3NlYVJlcy5zb2xldXMsIHVwPUYsIHBDdXRvZmYgPSBwYXJhbXMkcEN1dG9mZiwgbj0xMCkKdG9wUGF0aHdheXMgPC0gYmluZF9yb3dzKHRvcFVwLCB0b3BEb3duKSAlPiUKICBhcnJhbmdlKC1ORVMpICU+JQogIHB1bGwocGF0aHdheSkKCnBsb3RHc2VhVGFibGUoCiAgcGF0aHdheXMgPSBDR1BbdG9wUGF0aHdheXNdLAogIHN0YXRzID0gcmFua3Muc29sZXVzLAogIGZnc2VhUmVzID0gZmdzZWFSZXMuc29sZXVzLAogIGdzZWFQYXJhbSA9IDAuNSwKICByZW5kZXIgPSBUUlVFCikgJT4lCiAgZ2dwdWJyOjphc19nZ3Bsb3QoKSAjIG5lZWRlZCBzaW5jZSwgZm9yIHdoYXRldmVyIHJlYXNvbiBvbmx5IGBOVUxMYCBnZXRzIHJldHVybmVkIGlmIGBwbG90R3NlYVRhYmxlYCBpcyByZW5kZXJlZCBpbmxpbmUKYGBgCgojIyMgbW9zdCBkaWZmZXJlbnRpYWwgcmVndWxhdGVkIHBhdGh3YXlzLCBib3RoIHRpc3N1ZXMKCnVzaW5nIGBORVNgIGZyb20gdGhlIGZnc2VhIHJlc3VsdCBmaWx0ZXJpbmcgb24gdGhlIHNldCBgYGAgcEN1dG9mZj1gYHIgcGFyYW1zJHBDdXRvZmYgYGBgIHlpZWxkcyB0aGUgZm9sbG93aW5nIHBsb3Q6CgpgYGB7ciB0b3BEaWZmLmJvdGgsIHdhcm5pbmc9RkFMU0V9CgpwQ3V0b2ZmIDwtIHBhcmFtcyRwQ3V0b2ZmCmZnc2VhUmVzLmNvbWJpbmVkIDwtIG1lcmdlKAogIGRhdGEuZnJhbWUoZmdzZWFSZXMuZ2FzdHJvY1ssIGMoInBhdGh3YXkiLCAiTkVTIiwgInBhZGoiKV0pLAogIGRhdGEuZnJhbWUoZmdzZWFSZXMuc29sZXVzWywgYygicGF0aHdheSIsICJORVMiLCAicGFkaiIpXSksCiAgYnkgPSAicGF0aHdheSIsCiAgc3VmZml4ZXMgPSBjKCIuZ2EiLCAiLnNvbCIpCikgJT4lCiAgZmlsdGVyKHBhZGouZ2EgPCBwQ3V0b2ZmIHwgcGFkai5zb2wgPCBwQ3V0b2ZmKSAlPiUKICBtdXRhdGUoCiAgICBkaWZmLmV4cCA9IGNhc2Vfd2hlbigKICAgICAgTkVTLmdhICA8IDAgJiBORVMuc29sIDwgMCAmIHBhZGouZ2EgPCBwQ3V0b2ZmICYgcGFkai5zb2wgPCBwQ3V0b2ZmIH4gImJvdGggZG93biIsCiAgICAgIE5FUy5nYSAgPiAwICYgTkVTLnNvbCA+IDAgJiBwYWRqLmdhIDwgcEN1dG9mZiAmIHBhZGouc29sIDwgcEN1dG9mZiB+ICJib3RoIHVwIiwKICAgICAgTkVTLmdhICA8IDAgJiBORVMuc29sID4gMCAmIHBhZGouZ2EgPCBwQ3V0b2ZmICYgcGFkai5zb2wgPCBwQ3V0b2ZmIH4gImdhIGRvd24sIHNvbCB1cCIsCiAgICAgIE5FUy5nYSAgPiAwICYgTkVTLnNvbCA8IDAgJiBwYWRqLmdhIDwgcEN1dG9mZiAmIHBhZGouc29sIDwgcEN1dG9mZiB+ICJnYSB1cCwgc29sIGRvd24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkai5nYSA8IHBDdXRvZmYgJiBwYWRqLnNvbCA+IHBDdXRvZmYgfiAib25seSBnYXN0cm9jIiwKICAgICAgIyBORVMuZ2EgID4gMCAmICAgICAgICAgICAgICAgcGFkai5nYSA8IHBDdXRvZmYgJiBwYWRqLnNvbCA+IHBDdXRvZmYgfiAiZ2EgdXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkai5nYSA+IHBDdXRvZmYgJiBwYWRqLnNvbCA8IHBDdXRvZmYgfiAib25seSBzb2xldXMiLAogICAgICAjIE5FUy5zb2wgPiAwICYgICAgICAgICAgICAgICBwYWRqLmdhID4gcEN1dG9mZiAmIHBhZGouc29sIDwgcEN1dG9mZiB+ICJzb2wgdXAiLAogICAgICBUUlVFIH4gImRpZmZlcmVudCIKICAgICkKICApCgojIGZpbmFsIHBsb3QKcCA8LSBnZ3Bsb3QoZmdzZWFSZXMuY29tYmluZWQsIGFlcyh4ID0gTkVTLmdhLCB5ID0gTkVTLnNvbCwgdGV4dD1wYXRod2F5KSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRpZmYuZXhwKSkgKwogICMgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJjaGFydHJldXNlMSIsICJiaXNxdWUiLCAicm95YWxibHVlIikpICsKICBsYWJzKHggPSAiZ2FzdHJvYyIsIHkgPSAic29sZXVzIikgKwogICMgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChtYXgub3ZlcmxhcHMgPSAyMCkgKyAKICBnZ3RpdGxlKGxhYmVsID0gIk5FUyIpCgpwbG90bHk6OmdncGxvdGx5KHAsIHRvb2x0aXAgPSAiYWxsIikKYGBgCgojIyMgYmFycGxvdAoKYGBge3IgZGlmZkdlbmVzQmFyUGxvdH0KZ2dwbG90KGZnc2VhUmVzLmNvbWJpbmVkLCBhZXMoeCA9IGRpZmYuZXhwKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gZGlmZi5leHApKQpgYGAKCiMjIyBsaXN0IG9mIGFsbCBzaWduaWZpY2FudCBwYXRod2F5cwpwYXRod2F5cyB3aWxsIGJlIHNvcnRlZCBoZXJlIGJ5IHRoZSBhYnNvbHV0ZSBzdW0gb2YgdGhlIE5FUy4uLgoKIyMjIyBib3RoIGRvd24KYGBge3J9CmZnc2VhUmVzLmNvbWJpbmVkICU+JSAKICBmaWx0ZXIoZGlmZi5leHAgPT0gImJvdGggZG93biIpICU+JSAKICBtdXRhdGUoTkVTID0gYWJzKE5FUy5nYSkgKyBhYnMoTkVTLnNvbCkpICU+JSAKICBzZWxlY3QocGF0aHdheSwgTkVTKSAlPiUgCiAgYXJyYW5nZShkZXNjKE5FUykpICU+JQogIHJtYXJrZG93bjo6cGFnZWRfdGFibGUob3B0aW9ucyA9IGxpc3QoY29scy5taW4ucHJpbnQgPSAyKSkKYGBgCgojIyMjIGJvdGggdXAKYGBge3J9CmZnc2VhUmVzLmNvbWJpbmVkICU+JSAKICBmaWx0ZXIoZGlmZi5leHAgPT0gImJvdGggdXAiKSAlPiUgCiAgbXV0YXRlKE5FUyA9IGFicyhORVMuZ2EpICsgYWJzKE5FUy5zb2wpKSAlPiUgCiAgc2VsZWN0KHBhdGh3YXksIE5FUykgJT4lIAogIGFycmFuZ2UoZGVzYyhORVMpKSAlPiUKICBybWFya2Rvd246OnBhZ2VkX3RhYmxlKG9wdGlvbnMgPSBsaXN0KGNvbHMubWluLnByaW50ID0gMikpCmBgYAoKIyMjIyBvbmx5IGdhc3Ryb2MKYGBge3J9CmZnc2VhUmVzLmNvbWJpbmVkICU+JSAKICBmaWx0ZXIoZGlmZi5leHAgPT0gIm9ubHkgZ2FzdHJvYyIpICU+JSAKICBtdXRhdGUoTkVTID0gYWJzKE5FUy5nYSkgKyBhYnMoTkVTLnNvbCkpICU+JSAKICBzZWxlY3QocGF0aHdheSwgTkVTKSAlPiUgCiAgYXJyYW5nZShkZXNjKE5FUykpICU+JQogIHJtYXJrZG93bjo6cGFnZWRfdGFibGUob3B0aW9ucyA9IGxpc3QoY29scy5taW4ucHJpbnQgPSAyKSkKYGBgCgojIyMjIG9ubHkgc29sZXVzCmBgYHtyfQpmZ3NlYVJlcy5jb21iaW5lZCAlPiUgCiAgZmlsdGVyKGRpZmYuZXhwID09ICJvbmx5IHNvbGV1cyIpICU+JSAKICBtdXRhdGUoTkVTID0gYWJzKE5FUy5nYSkgKyBhYnMoTkVTLnNvbCkpICU+JSAKICBzZWxlY3QocGF0aHdheSwgTkVTKSAlPiUgCiAgYXJyYW5nZShkZXNjKE5FUykpICU+JQogIHJtYXJrZG93bjo6cGFnZWRfdGFibGUob3B0aW9ucyA9IGxpc3QoY29scy5taW4ucHJpbnQgPSAyKSkKYGBgCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CnNhdmVSRFMoZmdzZWFSZXMuY29tYmluZWQsIGZpbGUgPSAiLi9kYXRhL1JvYmplY3RzLzA0X2Znc2VhUmVzLmNvbWJpbmVkLnJkcyIpCmBgYAoKCiMgY3VycmVudFRPRE9zCgpbIF0gbG9va2luZyBhdCBkdXBsaWNhdGUgZW50cmV6Z2VuZV9pZHNcClsgXSBmaW5kaW5nIG9wdGltYWwgYG1heFNpemVgIChvbmUgc2lkZWQgY3VydmUpXApbIF0gZmluZCBvdXQgYmlvbG9naWNhbCBtZWFuaW5nIG9mIHNpZ25pZmljYW50IHBhdGh3YXlzXAo=